/**
******************************************************************************
  * @file           : UdpService.cpp
  * @author         : sally
  * @brief          : None
  * @attention      : None
  * @date           : 24-11-09
  ******************************************************************************
  */

#include "UdpService.h"

#include <cstring>

namespace droneswarm
{
    UdpService::UdpService(const std::string& ip, const uint16_t& port, uint8_t threadNum)
        : m_ip(ip),
          m_port(port),
          m_threadNums(threadNum)
    {
    }

    UdpService::~UdpService()
    {
        // 等待所有线程完成
        for (std::thread& t : m_threads)
        {
            if (t.joinable())
            {
                t.join();
            }
        }

        // 清理线程容器，确保没有悬空指针
        m_threads.clear();
        udpInfoMap.clear();
    }

    void UdpService::start()
    {
        for (uint8_t i = 0; i < m_threadNums; ++i)
        {
            m_threads.emplace_back(&UdpService::run_loop, this);
        }
    }

    void UdpService::stop()
    {
        std::cout << "UDP SERVER IS SHUTTING DOWN!!!" << std::endl;

        for (auto& info : udpInfoMap)
        {
            // 停止事件循环
            uv_stop(info.second->loop);

            // 触发 async 回调，因为uv_stop只是将uv的loop里面的停止标记为设置为true，但是其会阻塞在epoll_wait里面，我们使用uv_async_send
            //就是用来唤醒epoll_wait的，用来退出阻塞的
            uv_async_send(info.second->asyc);
            uv_loop_close(info.second->loop);
        }
    }

    bool UdpService::send_message(const std::string& ip, const uint16_t& port, const std::string& message)
    {
        auto* send_request = new AsyncSendRequest();
        send_request->message = std::make_shared<std::string>(message);
        //把数据放进缓冲区
        send_request->buffer = uv_buf_init(const_cast<char*>(send_request->message->c_str()),
                                           send_request->message->size());


        //将目的ip和端口进行转换
        struct sockaddr* destAddr = convert_Ip_Port_sockaddr(ip, port,AF_INET);
        send_request->addr = destAddr;

        //获取当前负载最低的一个loop
        std::shared_ptr<udpInfo_struct> min_load_loop = Minimum_load();
        if (min_load_loop == nullptr)
        {
            std::cerr << "Error: No available udpInfo found." << std::endl;
            delete send_request;
            return false;
        }
        send_request->send_socket = min_load_loop->socket;
        //这么做的目的就是让发送的回调函数中可以获取到send_request的数据
        min_load_loop->asyc->data = static_cast<void*>(send_request);

        int result = uv_async_send(min_load_loop->asyc);
        if (result != 0)
        {
            std::cerr << "uv_async_send failed: " << uv_strerror(result) << std::endl;
            delete send_request;
            return false;
        }

        std::future<bool> fut = send_request->promise.get_future();

        bool send_result = fut.get();
        if (send_result)
        {
            std::cout << "Message sent successful " << std::endl;
        }
        else
        {
            std::cerr << "Send error" << std::endl;
        }
        delete send_request;
        min_load_loop->asyc->data = nullptr;
        return send_result;
    }

    /**
     * @brief 这个函数的作用就是根据域名来进行UDP通信，uv_getaddrinfo函数是libuv库中的一个非阻塞型异步调用，会使用该库的默认线程池中的特定线程
     * 来完成此操作，解析完成后，事件循环线程会调用绑定的函数on_resolved来进行相关的操作
     * @param domain_name
     * @param port
     * @param message
     * @return
     */
    bool UdpService::send_messageByDomainName(const std::string& domain_name, const uint16_t port,
                                              const std::string& message)
    {
        uv_getaddrinfo_t* resolver = new uv_getaddrinfo_t();
        struct addrinfo hints;
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_DGRAM;

        std::shared_ptr<udpInfo_struct> min_load_loop = Minimum_load();
        if (min_load_loop == nullptr)
        {
            std::cerr << "Error: No available udpInfo found." << std::endl;
            return false;
        }

        auto send_request = new AsyncSendRequest();
        send_request->message = std::make_shared<std::string>(message);
        send_request->buffer = uv_buf_init(const_cast<char*>(send_request->message->c_str()),
                                           send_request->message->size());
        send_request->send_socket = min_load_loop->socket;
        // send_request->port = port;
        resolver->data = static_cast<void*>(send_request);
        std::string port_str = std::to_string(port);
        int ret = uv_getaddrinfo(min_load_loop->loop, resolver, on_resolved, domain_name.c_str(), port_str.c_str(),
                                 &hints);
        if (ret)
        {
            std::cerr << "uv_getaddrinfo error: " << uv_strerror(ret) << std::endl;
            delete resolver;
            delete send_request;
            return false;
        }

        //我们在这里异步等待发送的结果
        std::future<bool> fut = send_request->promise.get_future();

        bool send_result = fut.get();
        if (send_result)
        {
            std::cout << "Message sent successful " << std::endl;
        }
        else
        {
        }
        delete resolver;
        delete send_request;
        return send_result;
    }

    void UdpService::on_async_send(uv_async_t* handle)
    {
        if (handle->data == nullptr)
            return;
        auto send_request = static_cast<AsyncSendRequest*>(handle->data);
        if (send_request == nullptr)
        {
            return;
        }
        //这里的作用是使得on_send_request回调函数可以使用send_request的数据，就是将自身封装到send_req中，
        send_request->send_req.data = send_request;

        uv_udp_send(&send_request->send_req, send_request->send_socket, &send_request->buffer, 1, send_request->addr,
                    on_send_request);
    }

    void UdpService::on_send_request(uv_udp_send_t* req, int status)
    {
        auto send_request = static_cast<AsyncSendRequest*>(req->data);
        if (!status)
        {
            send_request->promise.set_value(true);
        }
        else
        {
            send_request->promise.set_value(false);
            std::cerr << "Send error: " << uv_strerror(status) << std::endl;
        }
    }

    void UdpService::on_resolved(uv_getaddrinfo_t* req, int status, struct addrinfo* res)
    {
        auto send_request = static_cast<AsyncSendRequest*>(req->data);
        if (status < 0)
        {
            std::cerr << "Getaddrinfo error: " << uv_strerror(status) << std::endl;
        }
        else
        {
            char addr[INET6_ADDRSTRLEN];
            if (res->ai_family == AF_INET)
            {
                struct sockaddr_in* ipv4 = reinterpret_cast<sockaddr_in*>(res->ai_addr);
                uv_ip4_name(ipv4, addr, sizeof(addr));
                std::string ipv4_str(addr);
                std::cout << "ipv4: " << ipv4_str << std::endl;
            }
            else if (res->ai_family == AF_INET6)
            {
                struct sockaddr_in6* ipv6 = reinterpret_cast<struct sockaddr_in6*>(res->ai_addr);
                uv_ip6_name(ipv6, addr, sizeof(addr));
                std::string ipv6_str(addr);
                std::cout << "ipv6: " << ipv6_str << std::endl;
            }
            //为了能够让发送完成的回调函数中访问到send_request，我们需要将send_request封装到send_req的data中
            send_request->send_req.data = send_request;

            uv_udp_send(&send_request->send_req, send_request->send_socket, &send_request->buffer, 1, res->ai_addr,
                        on_send_request);
        }

        uv_freeaddrinfo(res);
    }

    void UdpService::run_loop()
    {
        std::shared_ptr<udpInfo_struct> udp_info_struct = createUdpInfo(m_ip, m_port);

        uv_udp_bind(udp_info_struct->socket, (const struct sockaddr*)&udp_info_struct->addr, UV_UDP_REUSEADDR);
        uv_udp_set_broadcast(udp_info_struct->socket,ON);

        uv_udp_recv_start(udp_info_struct->socket, alloc_buffer, on_handle_received_data);

        uv_async_init(udp_info_struct->loop, udp_info_struct->asyc, on_async_send);

        {
            std::lock_guard<std::mutex> lock(mutex);
            udpInfoMap[std::this_thread::get_id()] = udp_info_struct;
            // std::cout<<"udpInfoMap size: "<<udpInfoMap.size()<<std::endl;
        }

        std::cout << "Server running on " << m_ip << ":" << m_port
            << " in thread " << std::this_thread::get_id() << std::endl;

        uv_run(udp_info_struct->loop, UV_RUN_DEFAULT);

        uv_loop_close(udp_info_struct->loop);
    }

    void UdpService::alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buff)
    {
        buff->base = new char[suggested_size];
        buff->len = suggested_size;;
    }

    void UdpService::on_handle_received_data(uv_udp_t* socket, ssize_t nread, const uv_buf_t* buff,
                                             const struct sockaddr* addr, unsigned flags)
    {
        if (nread < 0)
        {
            std::cerr << "Read error: " << uv_strerror(nread) << std::endl;
            uv_close((uv_handle_t*)socket, nullptr);
            return;
        }

        if (addr)
        {
            char sender[INET6_ADDRSTRLEN];
            int port;

            if (addr->sa_family == AF_INET)
            {
                struct sockaddr_in* addr_in = (struct sockaddr_in*)addr;
                uv_ip4_name(addr_in, sender, sizeof(sender));
                port = ntohs(addr_in->sin_port);
            }
            else if (addr->sa_family == AF_INET6)
            {
                struct sockaddr_in6* addr_in6 = (struct sockaddr_in6*)addr;
                uv_ip6_name(addr_in6, sender, sizeof(sender));
                port = ntohs(addr_in6->sin6_port);
            }

            std::cout << "Received data from " << sender << ":" << port << std::endl;
        }

        // 处理接收到的数据
        if (nread > 0)
        {
            std::string data(buff->base, nread);
            std::cout << "Data: " << data << std::endl;
        }

        // 清理缓冲区
        if (buff->base)
        {
            delete[] buff->base;
        }
    }


    struct sockaddr* UdpService::convert_Ip_Port_sockaddr(const std::string& ip, const uint16_t& port, int AF)
    {
        struct sockaddr* addr = nullptr;
        switch (AF)
        {
        case AF_INET:
            {
                struct sockaddr_in* addr_in = new sockaddr_in();
                memset(addr_in, 0, sizeof(sockaddr_in));

                addr_in->sin_family = AF_INET;
                addr_in->sin_port = htons(port);
                if (inet_pton(AF_INET, ip.c_str(), &addr_in->sin_addr) <= 0)
                {
                    delete addr_in;
                    return nullptr;
                }

                addr = reinterpret_cast<struct sockaddr*>(addr_in);
                break;
            }
        case AF_INET6:
            {
                struct sockaddr_in6* addr_in6 = new sockaddr_in6();
                memset(addr_in6, 0, sizeof(sockaddr_in6));

                if (inet_pton(AF_INET6, ip.c_str(), &addr_in6->sin6_addr) <= 0)
                {
                    delete addr_in6;
                    return nullptr;
                }
                addr = reinterpret_cast<struct sockaddr*>(addr_in6);
                break;
            }
        default:
            {
                std::cerr << "Wrong IP type value" << std::endl;
                break;
            }
        }
        return addr;
    }

    std::shared_ptr<udpInfo_struct> UdpService::Minimum_load()
    {
        if (udpInfoMap.empty())
            return nullptr;

        std::shared_ptr<udpInfo_struct> min = udpInfoMap.begin()->second;
        {
            std::lock_guard<std::mutex> lock(mutex);
            for (auto& pair : udpInfoMap)
            {
                if (min->load > pair.second->load)
                    min = pair.second;
            }
        }

        return min;
    }


    std::shared_ptr<struct udpInfo_struct> UdpService::createUdpInfo(const std::string& ip, const uint16_t& port)
    {
        std::shared_ptr<struct udpInfo_struct> udp_info = std::make_shared<udpInfo_struct>();
        uv_loop_t* loop = new uv_loop_t();
        uv_loop_init(loop);

        uv_udp_t* socket = new uv_udp_t();
        uv_udp_init(loop, socket);

        struct sockaddr_in addr;
        uv_ip4_addr(ip.c_str(), port, &addr);


        udp_info->loop = loop;
        udp_info->socket = socket;
        udp_info->addr = addr;
        udp_info->load = 0;
        udp_info->asyc = new uv_async_t();

        return udp_info;
    }
} // droneswarm
